﻿using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.IconLib;
using System.IO;
using System.Runtime.InteropServices;

namespace Wen.Core
{
	public static class IconHelpers
	{
		[DllImport("user32.dll")]
		private static extern uint GetWindowThreadProcessId(IntPtr hwnd, out int lpdwProcessId);

		[DllImport("user32", CharSet = CharSet.Auto, SetLastError = true)]
		private static extern IntPtr SendMessage(IntPtr hwnd, UInt32 msg, IntPtr wparam, IntPtr lparam);

		[DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "GetClassLong", SetLastError = true)]
		private static extern IntPtr GetClassLongPtr(IntPtr hWnd, int nIndex);

		private const UInt32 WmGeticon = 0x7F;
		private const int IconBig = 1; //(32x32)
		public static int IconSmall = 0; //(16x16)
		public static int IconSmall2 = 2;
		private const int GclHicon = -14;
		private const int GclHiconSm = -34;

		public static Bitmap GetIcon(IntPtr hwnd)
		{
			Bitmap temp = null;
			String exename = GetPathFromWindowHandler(hwnd);

			if (!String.IsNullOrEmpty(exename))
				temp = GetBestIconBitmap(exename);
			return temp ?? GetWindowBitmap(hwnd);
		}

		private static Bitmap GetWindowBitmap(IntPtr pt)
		{
			IntPtr iconHandle = SendMessage(pt, WmGeticon, new IntPtr(IconBig), new IntPtr(0));
			if (iconHandle == IntPtr.Zero)
				iconHandle = SendMessage(pt, WmGeticon, new IntPtr(IconSmall), new IntPtr(0));
			if (iconHandle == IntPtr.Zero)
				iconHandle = SendMessage(pt, WmGeticon, new IntPtr(IconSmall2), new IntPtr(0));
			if (iconHandle == IntPtr.Zero)
				iconHandle = GetClassLongPtr(pt, GclHicon);
			if (iconHandle == IntPtr.Zero)
				iconHandle = GetClassLongPtr(pt, GclHiconSm);
			if (iconHandle != IntPtr.Zero) {
				Bitmap bmpIcon = Bitmap.FromHicon(iconHandle);
				return bmpIcon;
			}
			return null;
		}

		private static String GetPathFromWindowHandler(IntPtr hwnd) {
			String filename = String.Empty;

			try {
				int processid;
				GetWindowThreadProcessId(hwnd, out processid);
				Process proc = Process.GetProcessById(processid);
				filename = proc.MainModule.FileName;
			}
			catch (System.ComponentModel.Win32Exception) {

			}

			return filename;
		}

		private static Bitmap GetBestIconBitmap(String path, int maxHeight = 256, int rank = 0) {
			IconImage bestIcon = null;

			if (!File.Exists(path)) return null;
			switch (Path.GetExtension(path).ToLower()) {
				case ".ico":
				case ".icl":
				case ".dll":
				case ".exe":
				case ".ocx":
				case ".cpl":
				case ".src":
					//Les extensions si dessus sont gérés
					break;
				default: return null;
			}

			//Ouverture de l'exécutable
			MultiIcon iconList = new MultiIcon();
			iconList.Load(path);

			if (iconList.Count > rank) {
				//Recherche de la meilleure definition disponible (pour l'icone placée à la position rank)
				long bestscore = 0;

				foreach (IconImage icon in iconList[rank]) {
					//Donne arbitrairement un score en cumulant le format de pixels et la taille de l'image
					long score = (icon.Size.Height * icon.Size.Width * GetIconRes(icon.PixelFormat));

					if (icon.Size.Height <= maxHeight && score > bestscore) {
						bestscore = score;
						bestIcon = icon;
					}
				}
			}
			return bestIcon == null ? null : bestIcon.Icon.ToBitmap();
		}

		private static int GetIconRes(System.Drawing.Imaging.PixelFormat pixelFormat) {
			switch (pixelFormat) {
				case System.Drawing.Imaging.PixelFormat.Format1bppIndexed:
					return 1;
				case System.Drawing.Imaging.PixelFormat.Format24bppRgb:
					return 24;
				case System.Drawing.Imaging.PixelFormat.Format32bppArgb:
				case System.Drawing.Imaging.PixelFormat.Format32bppRgb:
					return 32;
				case System.Drawing.Imaging.PixelFormat.Format8bppIndexed:
					return 8;
				case System.Drawing.Imaging.PixelFormat.Format4bppIndexed:
					return 4;
			}
			return 0;
		}
	}
}
